/*
 * This file is part of PDBEdit.
 * 
 * PDBEdit is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * PDBEdit is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with PDBEdit.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * Creation date : 17/11/2010
 * Copyright (c) Guerfyld 2010. All rights reserved.
 * http://......
 */
package tablelist;

import java.util.Vector;
import javax.microedition.lcdui.*;

public class TableList extends Canvas {

    private String tableName;
    private TableSource source;
    private int oneRowHeight;
    private int rowCount;
    private int colCount;
    protected int focusRow = 0;
    protected int focusCol = 0;
    private int rowOffset = 0;
    private int colOffset = 0;
    private int headerHeight;
    private String header[];
    private int colWidth[];
    private int rowHeight[];
    private TableCell cells[][];
    private Font font = Font.getDefaultFont();
    private Command selectCommand = null;
    private CommandListener listener = null;
    private int yPos;
    private int xPos;

    public TableList(String tableName, TableSource source) {
        this.tableName = tableName;
        this.source = source;
        initialize();
        repaint();
    }

    private void initialize() {
        setTitle(tableName);
        this.colWidth = source.getColWidth();
        this.header = source.getHeader();
        rowCount = source.getRowCount();
        colCount = source.getColCount();  
	this.cells = new TableCell[rowCount][colCount];
        for (int i = 0; i < rowCount; i++) {
	   for (int j = 0; j < colCount; j++) {
	      this.cells[i][j] = new TableCell(source.getValue(i, j));
           }
        }
	this.rowHeight = new int[rowCount];
        calculateHeight();
        repaint();
    }
    
    /*
     * Set table's font
     */
    public void setFont(Font font) {
        this.font = font;
        calculateHeight();
        repaint();
    }
    
    /*
     *This method calculate all cells' height.
     *Long cell text will be splited into several segments(several lines).
     */
    protected void calculateHeight() {
        oneRowHeight = font.getHeight() + 1;
        if(header==null){
            headerHeight=0;
        }else{
            headerHeight = oneRowHeight;
        }

        int xTemp = 0;
        int yTemp = headerHeight;      
        int height= oneRowHeight;
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < rowCount; i++) {
            rowHeight[i] = oneRowHeight;
            xTemp = 0;

            for (int j = 0; j < colCount; j++) {
                cells[i][j].x = xTemp;
                xTemp += colWidth[j];
                cells[i][j].y = yTemp;

                if (cells[i][j].cellText == null || font.stringWidth(cells[i][j].cellText) < colWidth[j]) {
                    cells[i][j].multiLine = false;
                    height = oneRowHeight;
                } else {
                    cells[i][j].multiLine = true;
                    cells[i][j].cellStrings = new Vector();
                    sb.setLength(0);
                    for (int k = 0; k < cells[i][j].cellText.length(); k++) {
                        sb.append(cells[i][j].cellText.charAt(k));
                        if (font.stringWidth(sb.toString()) > colWidth[j]) {
                            sb.deleteCharAt(sb.length() - 1);
                            cells[i][j].cellStrings.addElement(sb.toString());
                            sb.setLength(0);
                            sb.append(cells[i][j].cellText.charAt(k));
                        }
                    }
                    if (sb.length() > 0) {
                        cells[i][j].cellStrings.addElement(sb.toString());
                    }
                    height = oneRowHeight * cells[i][j].cellStrings.size();
                }

                if (height > rowHeight[i]) {
                    rowHeight[i] = height;
                }
            }
            yTemp += rowHeight[i];
        }
    }

    protected void paint(Graphics g) {
        g.setFont(font);
        //draw table background
        g.setColor(0xffffff);
        g.fillRect(0, 0, this.getWidth(), this.getHeight());

        //draw table border line
        g.setColor(0x000000);
        g.drawLine(0, 0, cells[0][colCount-1].x+colWidth[colCount-1], 0);
//        g.drawLine(0, 0, 0, cells[rowCount-1][0].y+rowHeight[rowCount-1]);
        g.drawLine(cells[0][colCount-1].x+colWidth[colCount-1], 0, cells[0][colCount-1].x+colWidth[colCount-1], cells[rowCount-1][0].y+rowHeight[rowCount-1]);
        g.drawLine(0, cells[rowCount-1][0].y+rowHeight[rowCount-1], cells[0][colCount-1].x+colWidth[colCount-1], cells[rowCount-1][0].y+rowHeight[rowCount-1]);

        //draw cells
        for (int i = 0; i < rowCount; i++) {
            //draw cell background
            if (i == focusRow) {
                // Focus line
                g.setColor(0x3a9ff7);
                g.fillRect(cells[i][0].x - colOffset + 1, cells[i][0].y - rowOffset + 1, cells[i][colCount - 1].x + colWidth[colCount - 1], rowHeight[i] - 1);
            }
            else if (i % 2 == 0) {
                g.setColor(0xe7f3f8);
                g.fillRect(cells[i][0].x - colOffset + 1, cells[i][0].y - rowOffset + 1, cells[i][colCount - 1].x + colWidth[colCount - 1], rowHeight[i] - 1);
            }
            g.setColor(0x000000);
            g.drawLine(cells[i][0].x - colOffset, cells[i][0].y - rowOffset + rowHeight[i], cells[i][colCount - 1].x + colWidth[colCount - 1] - colOffset, cells[i][0].y + rowHeight[i] - rowOffset);
            
            //draw cell text
            for (int j = 0; j < colCount; j++) {
                //draw single-line text
                if (!cells[i][j].multiLine) {
                    if (cells[i][j].cellText != null) {
                        g.drawString(cells[i][j].cellText, cells[i][j].x - colOffset + 1, cells[i][j].y - rowOffset + 1, 0);
                    }
                } else {
                    //draw multi-line text
                    for (int a = 0; a < cells[i][j].cellStrings.size(); a++) {
                        g.drawString(cells[i][j].cellStrings.elementAt(a).toString(), cells[i][j].x - colOffset + 1, cells[i][j].y + oneRowHeight * a - rowOffset + 1, 0);
                    }
                }
            }
        }
        //draw table header
        if (header != null) {
            g.setColor(0xA0A0A0);
            g.fillRect(0 - colOffset + 1, 1, cells[0][colCount - 1].x + colWidth[colCount - 1], headerHeight);
            g.setColor(0x000000);
            g.drawLine(0 - colOffset, 0, cells[0][colCount - 1].x + colWidth[colCount - 1], 0);
            g.drawLine(0 - colOffset, headerHeight, cells[0][colCount - 1].x + colWidth[colCount - 1], headerHeight);
            for (int i = 0; i < header.length; i++) {
                g.drawString(header[i], cells[0][i].x - colOffset + 1, 0, 0);
            }
        }
        
        //draw vertical line
        int temp = 0;
        g.drawLine(0 - colOffset, 0, 0 - colOffset, cells[rowCount - 1][0].y + rowHeight[rowCount - 1]);
        for (int i = 0; i < colWidth.length; i++) {
            temp += colWidth[i];
            g.drawLine(temp - colOffset, 0, temp - colOffset, cells[rowCount - 1][0].y + rowHeight[rowCount - 1]);
        }

        //draw focus cell
        //g.setColor(0x3a9ff7);        
        //g.fillRect(cells[focusRow][focusCol].x - colOffset + 1, cells[focusRow][focusCol].y - rowOffset + 1, colWidth[focusCol] - 1, rowHeight[focusRow] - 1);
        //g.setColor(0x000000);
        //if (!cells[focusRow][focusCol].multiLine) {
        //    if (cells[focusRow][focusCol].cellText != null) {
        //        g.drawString(cells[focusRow][focusCol].cellText, cells[focusRow][focusCol].x - colOffset + 1, cells[focusRow][focusCol].y - rowOffset + 1, 0);
        //    }
        //} else {
        //    for (int i = 0; i < cells[focusRow][focusCol].cellStrings.size(); i++) {
        //        g.drawString(cells[focusRow][focusCol].cellStrings.elementAt(i).toString(), cells[focusRow][focusCol].x - colOffset + 1, cells[focusRow][focusCol].y + oneRowHeight * i - rowOffset + 1, 0);
        //    }
        //}

        //draw vertical scroll bar
        g.setColor(0x000000);
        g.fillRect(this.getWidth() - 2, headerHeight, 2, this.getHeight() - headerHeight - 1);
        
	int scrH = this.getHeight() - headerHeight - 3;
	int scrP = headerHeight + 1;
	if (rowCount != 0) {
		scrH = scrH / rowCount;
		if (scrH < 1) scrH = 1;
		scrP += ((this.getHeight() - headerHeight - 1) * focusRow) / rowCount;
	}	
        g.setColor(0xFFFFFF);        
        g.fillRect(this.getWidth()-1, scrP, 1, scrH);

        //draw horizontal scroll bar
        g.setColor(0x000000);
        g.fillRect(0, this.getHeight() - 2, this.getWidth() - 1, 2);
        
	int scrW = this.getWidth() - 3;
	scrP = 0;
	if (colCount != 0) {
		scrW = scrW / colCount;
		if (scrW < 1) scrW = 1;
		scrP += ((this.getWidth() - 1) * focusCol) / colCount;
	}	
        g.setColor(0xFFFFFF);        
        g.fillRect(scrP, this.getHeight()-1, scrW, 1);
    }

    public void keyPressed(int keyCode) {
        switch (getGameAction(keyCode)) {
            // case -3: //left
            case LEFT:
                focusCol--;
                if (focusCol <= 0) {
                    focusCol = 0;
                }
                if (cells[0][focusCol].x < colOffset) {
                    colOffset = cells[0][focusCol].x;
                }
                break;

            //case -4: //right
            case RIGHT:

                focusCol++;
                if (focusCol >= colCount - 1) {
                    focusCol = colCount - 1;
                }
                if (cells[0][focusCol].x + colWidth[focusCol]-colOffset > this.getWidth()) {
                    colOffset = cells[0][focusCol].x + colWidth[focusCol] - this.getWidth() +1;
                }

                break;

            //case -1:  //up
            case UP:

                focusRow--;
                if (focusRow <= 0) {
                    focusRow = 0;
                }
                if (cells[focusRow][0].y < rowOffset+headerHeight) {
                    rowOffset = cells[focusRow][0].y-headerHeight;
                }

                break;

            //case -2: //down
            case DOWN:

                focusRow++;
                if (focusRow >= rowCount - 1) {
                    focusRow = rowCount - 1;
                }

                if (cells[focusRow][0].y + rowHeight[focusRow]-rowOffset> this.getHeight()) {
                    rowOffset = cells[focusRow][0].y + rowHeight[focusRow] - this.getHeight();
                }

                break;

            case GAME_A: // Scroll up

                for (int i = focusRow; i >= 0; i--) {
                   focusRow = i;
                   if (cells[focusRow][0].y < rowOffset+headerHeight) {
                      rowOffset = cells[focusRow][0].y - headerHeight;

                      int height = headerHeight+rowHeight[focusRow];
                      for (int j = focusRow-1; j >=0; j--) {
                          if (height+rowHeight[j] <= this.getHeight()) {
                              focusRow = j;
                              rowOffset -= rowHeight[focusRow];
                              height += rowHeight[focusRow];
                          }
                          else
                              break;
                      }
                      break;
                   }
                }

                break;

            case GAME_C: // Scroll down

                for (int i = focusRow; i < rowCount; i++) {
                   focusRow = i;
                   if (cells[focusRow][0].y+rowHeight[focusRow]-rowOffset> this.getHeight()) {
                      rowOffset = cells[focusRow][0].y+rowHeight[focusRow] - this.getHeight();

                      int height = headerHeight+rowHeight[focusRow];
                      for (int j = focusRow+1; j < rowCount; j++) {
                          if (height+rowHeight[j] <= this.getHeight()) {
                              focusRow = j;
                              rowOffset += rowHeight[focusRow];
                              height += rowHeight[focusRow];
                          }
                          else
                              break;
                      }
                      break;
                   }
                }

                break;

            case GAME_B: // Scroll left

                for (int i = focusCol; i >= 0; i--) {
                   focusCol = i;
                   if (cells[0][focusCol].x < colOffset) {
                      colOffset = cells[0][focusCol].x;

                      int width = colWidth[focusCol];
                      for (int j = focusCol-1; j >=0; j--) {
                          if (width+colWidth[j] <= this.getWidth()) {
                              focusCol = j;
                              colOffset -= colWidth[focusCol];
                              width += colWidth[focusCol];
                          }
                          else
                              break;
                      }
                      break;
                   }
                }

                break;

            case GAME_D: // Scroll right

                for (int i = focusCol; i < colCount; i++) {
                   focusCol = i;
                   if (cells[0][focusCol].x+colWidth[focusCol]-colOffset> this.getWidth()) {
                      colOffset = cells[0][focusCol].x+colWidth[focusCol] - this.getWidth();

                      int width = colWidth[focusCol];
                      for (int j = focusCol+1; j < colCount; j++) {
                          if (width+colWidth[j] <= this.getWidth()) {
                              focusCol = j;
                              colOffset += colWidth[focusCol];
                              width += colWidth[focusCol];
                          }
                          else
                              break;
                      }
                      break;
                   }
                }

		break;

            //case 13: // Select
            //case -5:
            case FIRE:
		if (listener != null && selectCommand != null) {
                    listener.commandAction(selectCommand, this);
                }
                break;
        }
        repaint();
    }

    public void setSelectCommand(Command command) {
        selectCommand = command;
        addCommand(command);
    }

    public void setCommandListener(CommandListener l) {
        super.setCommandListener(l);
        listener = l;
    }

    public int getSelectedIndex() {
        return focusRow;
    }

    public void setSelectedIndex(int row) {
        focusCol = 0;
        colOffset = 0;

        focusRow = row;
        if (focusRow >= rowCount - 1) {
           focusRow = rowCount - 1;
        }

        rowOffset = cells[focusRow][0].y - headerHeight;
        repaint();
    }
	
	protected void pointerPressed(int x, int y) {
		xPos = x;
		yPos = y;
	}

	protected void pointerReleased(int x, int y) {
		if (y - yPos > 20) {
			keyPressed(getKeyCode(GAME_A));
		}
		else if (y - yPos < -20) {
			keyPressed(getKeyCode(GAME_C));
		}
		else if (x - xPos > 20) {
			keyPressed(getKeyCode(GAME_B));
		}
		else if (x - xPos < -20) {
			keyPressed(getKeyCode(GAME_D));
		}
		else {
			int precRow = focusRow;
			int precCol = focusCol;
			for (int i = 0; i < rowCount; i++) {
				if (this.cells[i][0].y-rowOffset > yPos) {
					focusRow = i -1;
					for (int j = 0; j < colCount; j++) {
						if (this.cells[0][j].x-colOffset > xPos) {
							focusCol = j-1;
							break;
						}
					}
					break;
				}
			}
			if ((precRow == focusRow) && (precCol == focusCol)) {
				keyPressed(getKeyCode(FIRE));
			} else {
				repaint();
			}
		}
	}
}